#include "RenzoMustHave.h"
#include "RenzoPrecompiled.h"
#include "RenzoUtil.h"
#include <stdio.h>

//using namespace Renzo;

namespace Renzo
{
	// need export these variables from RenzoUtil.h in Renzoderer.dll
	// here I'd rather remove "extern" keyword to let these variables declare again in RenzoSystemTest.exe. Note that they are a separate set of variables from those with the same name in Renzoderer.dll.
	/*extern*/ unsigned __int64 rzStartCycles, rzEndCycles, rzOverheadCycles, rzElapsedCycles;
	/*extern*/ const int numIterations = 10000;

	void rzTestFloat4_InheritanceAddress() {
		float4 f;
		printf("Address of x: %d\n", &f.x);
		printf("Address of y: %d\n", &f.y);
		printf("Address of z: %d\n", &f.z);
		printf("Address of w: %d\n", &f.w);

	}

	void rzTestFloat4_AddSSE_Unaligned() {
		printf("rzTestFloat4_AddSSE_Unaligned\n");
		printf("-----------------------------\n");
		float4 a(1.0f, 2.0f, 3.0f, 4.0f);
		float4 b(5.0f, 6.0f, 7.0f, 8.0f);
		float4 c;

		// C++	
		rzElapsedCycles = 0;
		for (int i = 0; i < numIterations; ++i) {
			rzStartCycles = rzGetClockCycles();
			c = a + b;
			rzEndCycles = rzGetClockCycles();
			rzElapsedCycles += rzEndCycles - rzStartCycles - rzOverheadCycles;
		}
		printf("CPU: c = (%f, %f, %f, %f)\n", c.x, c.y, c.z, c.w);
		printf("Cycles: %I64u\n", rzElapsedCycles / numIterations);

		// SSE
		const float* pA = a.getPtr();
		const float* pB = b.getPtr();
		//float d[4];
		//float* pC = d;
		float pC[4];
		//float* pC = (float*) malloc(sizeof(float) * 4);
		
		rzElapsedCycles = 0;
		for (int i = 0; i < numIterations; ++i) {
			rzStartCycles = rzGetClockCycles();
			__asm {		
				//------------------------------------------------------------------------
				// In assembly, float* p and float p[4] is not the same.
				// mov p, ... -> move something into p array if p is float[4] (stack)
				// mov [p], ... is the same as mov p.
				//
				// mov eax, p
				// mov [eax], ... -> move something into p array if p is float* (pointer)
				//------------------------------------------------------------------------
				// unaligned version: why cannot move directly like this: movups xmm0, [pA]
				mov eax, pA			// must load pointer to register EAX first, then perform  the sum
				movups xmm0, [eax]
				
				mov eax, pB
				movups xmm1, [eax]

				addps xmm0, xmm1

				//mov eax, pC
				//movups [eax], xmm0

				movups pC, xmm0
				//movups [pC], xmm0
			}
			rzEndCycles = rzGetClockCycles();
			rzElapsedCycles += rzEndCycles - rzStartCycles - rzOverheadCycles;	
		}
		printf("SSE (unaligned data): c = (%f, %f, %f, %f)\n", pC[0], pC[1], pC[2], pC[3]);
		printf("Cycles: %I64u\n", rzElapsedCycles / numIterations);
		printf("\n");
		//free(pC);
	}

	void rzTestFloat4_AddSSE_Aligned() {
		printf("rzTestFloat4_AddSSE_Aligned\n");
		printf("-----------------------------\n");
		float4 a(1.0f, 2.0f, 3.0f, 4.0f);
		float4 b(5.0f, 6.0f, 7.0f, 8.0f);

		float* alignedMem = (float*)_aligned_malloc(sizeof(float) * 12, 16);
		float* pA = alignedMem;
		float* pB = pA + 4;
		float* pC = pB + 4;
		memcpy(pA, a.getPtr(), sizeof(float) * 4);
		memcpy(pB, b.getPtr(), sizeof(float) * 4);
		
		rzElapsedCycles = 0;
		for (int i = 0; i < numIterations; ++i) {
			rzStartCycles = rzGetClockCycles();	
			__asm {
				// aligned version		
				/*
				movaps xmm0, [pA]		
				movaps xmm1, [pB]
				addps xmm0, xmm1		
				movaps [pC], xmm0		
				*/

				// aligned version: why cannot move directly from pA to xmm0?
				mov eax, pA
				movaps xmm0, [eax]
				mov eax, pB
				movaps xmm1, [eax]
				addps xmm0, xmm1
				mov eax, pC
				movaps [eax], xmm0
			}
			rzEndCycles = rzGetClockCycles();
			rzElapsedCycles += rzEndCycles - rzStartCycles - rzOverheadCycles;	
		}
		printf("SSE (aligned data): c = (%f, %f, %f, %f)\n", pC[0], pC[1], pC[2], pC[3]);
		printf("Cycles: %I64u\n", rzElapsedCycles / numIterations);
		printf("\n");
		_aligned_free(alignedMem);
	}

	void my_print(char* str) {
		printf(str);
	}

	void rzTestFloat4_HelloWorldInlineASM() {	
		char greet[] = "Hello, World!";
		char format[] = "%s\n";
		char* pGreet = greet;
		char* pFormat = format;

		// printf(format, greet);
		__asm {	
			//----------------------------------------------------------------------------------------------------
			// greet: string array
			// offset greet: string address (global)
			//----------------------------------------------------------------------------------------------------
			//mov eax, offset greet			// use offset keyword and define greet and format as GLOBAL variables
			mov eax, pGreet					// use C pointer and can define greet and format as LOCAL variables
			//mov eax, offset greet[0]		// not work		
			push eax
			
			//mov eax, offset format
			mov eax, pFormat
			push eax

			//----------------------------------------------------------------------------------------------------
			// @description
			// Properties -> C/C++ -> Code Generation -> Runtime Library = /MT
			// Cannot let it call from DLL!?
			// http://social.msdn.microsoft.com/Forums/en-US/vcgeneral/thread/a6619386-d8ce-4d59-87c9-c5969230f290
			//----------------------------------------------------------------------------------------------------
			call printf		
			//call dword ptr printf
			//call puts
			//call printf_s
			//call my_print
			
			// clean stack		
			pop eax
			pop eax
		}
	}

}